46. Python条件语句和循环语句

⭐ 本章学习目标

  • 掌握Python三种基本控制结构:顺序、选择、循环
  • 理解if-elif-else条件语句的语法与应用
  • 熟练使用for循环遍历各种数据结构
  • 掌握while循环的使用场景与注意事项
  • 理解breakcontinuepass控制语句
  • 综合运用控制结构完成股票筛选等商业分析任务

⭐ 控制流的三种基本结构

任何算法都可以用三种基本控制结构表示:

结构类型 英文名 说明 Python关键字
顺序结构 Sequence 语句按顺序执行
选择结构 Selection 根据条件选择执行路径 if-elif-else
循环结构 Iteration 重复执行代码块 forwhile

这三种结构构成了结构化程序设计的基础(Dijkstra, 1968)。

⭐ 布尔逻辑与条件判断

Python的条件判断基于布尔代数(George Boole, 1854):

运算 数学符号 Python运算
与(AND) \(a \land b\) a and b
或(OR) \(a \lor b\) a or b
非(NOT) \(\neg a\) not a

短路径求值False and f() 不会调用 f(),直接返回 False

⭐ if条件语句:基本语法

Listing 1
# 场景:基于移动平均线的交易策略
# 定义关键价格指标
price = 18.50    # 当前股价
ma20 = 18.00     # 20日移动平均线
ma60 = 17.50     # 60日移动平均线

# if语句:条件判断与分支执行
# 语法: if condition1: elif condition2: ... else:
# if、elif、else后的冒号(:)表示代码块开始

# 条件1:价格同时高于MA20和MA60
# and逻辑运算符:两个条件都为True时,整体为True
if price > ma20 and price > ma60:
    signal = '买入'
    # 缩进的代码块属于if语句
    # Python使用缩进(通常4个空格)表示代码块层次

# 条件2:价格高于MA20但低于MA60
# elif是"else if"的缩写,用于处理多个互斥条件
elif price > ma20:
    signal = '持有'

# 条件3:价格低于MA60
elif price < ma60:
    signal = '卖出'

# else:以上条件都不满足时执行
# 在这里是:价格介于MA60和MA20之间
else:
    signal = '观望'

# 输出交易信号
# f-string是Python 3.6+的格式化字符串语法
print(f'交易信号: {signal}')
# 输出: 交易信号: 买入
交易信号: 买入

⭐ if语句语法要点

  • ifelifelse 后必须加冒号(:
  • Python使用缩进(通常4个空格)表示代码块层次
  • elifelse if 的缩写,处理多个互斥条件
  • else 处理以上条件都不满足的情况
  • and 逻辑运算符:两个条件都为 True 时整体为 True

⭐ 真值测试规则

# Python中的真假值判定规则
# 以下值被判定为False:
# False、None、数值0(0, 0.0)、空字符串('')、空容器([], {}, set())

# 示例:检查持仓是否为空
position = 0
if position:
    print(f'持仓{position}股')
else:
    print('空仓')  # 会执行这个分支

# 显式比较(推荐写法,更清晰)
if position != 0:
    print(f'持仓{position}股')
else:
    print('空仓')
空仓
空仓

⭐ 链式比较与三元运算符

# 1. 链式比较(Python风格,更简洁)
price = 15.0

if 10 < price < 20:
    print('价格在10到20之间')

# 等价于: if price > 10 and price < 20

# 2. 三元运算符: 值1 if 条件 else 值2
amount = 10000
commission_rate = 0.0003 if amount > 10000 else 0.0005
print(f'手续费率: {commission_rate:.4f}')

# 等价的if-else语句:
# if amount > 10000:
#     commission_rate = 0.0003
# else:
#     commission_rate = 0.0005
价格在10到20之间
手续费率: 0.0005

⭐ 嵌套if的优化:卫语句

# 不推荐:深层嵌套,可读性差
price = 18.5
volume = 1000000
ma20 = 18.00

# 推荐:提前返回(卫语句),逻辑更清晰
def generate_signal(price, volume, ma20):
    if price <= 18:
        return '不操作'
    if volume <= 500000:
        return '观望'
    if price > ma20:
        return '强力买入'
    return '考虑买入'

signal = generate_signal(price, volume, ma20)
print(f'交易信号: {signal}')
交易信号: 强力买入

⭐ 金融应用:多条件筛选

# 股票筛选策略
stock = {
    'name': '招商银行',
    'price': 45.2,
    'pe': 8.5,      # 市盈率
    'pb': 0.8,      # 市净率
    'roe': 0.15     # 净资产收益率
}

# 多条件筛选
conditions = [
    stock['price'] < 100,
    stock['pe'] < 10,
    stock['pb'] < 1,
    stock['roe'] > 0.10
]

# all()函数:所有条件为True时返回True
if all(conditions):
    print(f'{stock["name"]} 符合价值投资标准')
else:
    print(f'{stock["name"]} 不符合标准')
招商银行 符合价值投资标准

⭐ for循环:遍历数据结构

Listing 2
# 1. 遍历列表
# 创建股票代码列表
stocks = ['600519.SH', '000858.SZ', '600036.SH']

print('股票代码:')
# for循环:遍历序列中的每个元素
# 语法: for item in iterable:
for code in stocks:
    # code依次取stocks中的每个元素
    print(f'  {code}')
# 输出:
#   600519.SH
#   000858.SZ
#   600036.SH

# 2. 遍历字典
stock_dict = {'600519.SH': '贵州茅台', '000858.SZ': '五粮液'}

print('\n股票名称:')
# items()方法返回(键, 值)元组的迭代器
# 这允许同时遍历键和值
for code, name in stock_dict.items():
    # 每次迭代,code和name分别取键和值
    print(f'  {code}: {name}')

# 3. enumerate()获取索引和元素
print('\n带索引的遍历:')
# enumerate()返回(索引, 元素)的迭代器
# start参数指定索引起始值,默认为0
for i, code in enumerate(stocks):
    # i从0开始,依次为0, 1, 2
    print(f'  第{i+1}个: {code}')
# 输出:
#   第1个: 600519.SH
#   第2个: 000858.SZ
#   第3个: 600036.SH
股票代码:
  600519.SH
  000858.SZ
  600036.SH

股票名称:
  600519.SH: 贵州茅台
  000858.SZ: 五粮液

带索引的遍历:
  第1个: 600519.SH
  第2个: 000858.SZ
  第3个: 600036.SH

⭐ for循环语法要点

  • 语法for item in iterable:
  • 可遍历的对象:列表、字典、元组、字符串、range等
  • enumerate():同时获取索引和元素
  • dict.items():同时遍历字典的键和值
  • dict.keys():只遍历键
  • dict.values():只遍历值

⭐ range() 函数详解

# range()生成整数序列,常用于for循环
# 语法: range(start, stop, step)

# 用法1:指定结束值(从0开始)
print('range(5):', list(range(5)))

# 用法2:指定起始和结束值
print('range(2, 5):', list(range(2, 5)))

# 用法3:指定步长
print('range(0, 10, 2):', list(range(0, 10, 2)))

# 注意:range()不包含结束值(左闭右开)
range(5): [0, 1, 2, 3, 4]
range(2, 5): [2, 3, 4]
range(0, 10, 2): [0, 2, 4, 6, 8]

⭐ zip() 函数:并行遍历

# zip()可以同时遍历多个序列
codes = ['600519.SH', '000858.SZ', '600036.SH']
prices = [1850, 220, 45]

# 同时遍历代码和价格
for code, price in zip(codes, prices):
    print(f'{code}: {price}元')

print()

# 使用enumerate和zip组合
for i, (code, price) in enumerate(zip(codes, prices)):
    print(f'{i+1}. {code}: {price}元')
600519.SH: 1850元
000858.SZ: 220元
600036.SH: 45元

1. 600519.SH: 1850元
2. 000858.SZ: 220元
3. 600036.SH: 45元

⭐ 字典的多种遍历方式

portfolio = {
    '600519.SH': {'name': '茅台', 'shares': 100},
    '000858.SZ': {'name': '五粮液', 'shares': 200}
}

# 方式1:遍历键
print('股票代码:')
for code in portfolio:
    print(f'  {code}')

# 方式2:遍历键值对
print('\n股票详情:')
for code, info in portfolio.items():
    print(f'  {code}: {info["name"]}, {info["shares"]}股')

# 方式3:遍历值
print('\n股票名称:')
for info in portfolio.values():
    print(f'  {info["name"]}')
股票代码:
  600519.SH
  000858.SZ

股票详情:
  600519.SH: 茅台, 100股
  000858.SZ: 五粮液, 200股

股票名称:
  茅台
  五粮液

⭐ 列表推导式

# 列表推导式是for循环的简洁表达
# 语法: [expression for item in iterable if condition]

# 传统for循环
squares = []
for i in range(10):
    squares.append(i ** 2)
print('传统循环:', squares[:5], '...')

# 列表推导式(Python风格,更简洁)
squares = [i ** 2 for i in range(10)]
print('列表推导:', squares[:5], '...')

# 带条件的列表推导式
even_squares = [i ** 2 for i in range(10) if i % 2 == 0]
print('偶数平方:', even_squares)
传统循环: [0, 1, 4, 9, 16] ...
列表推导: [0, 1, 4, 9, 16] ...
偶数平方: [0, 4, 16, 36, 64]

⭐ 列表推导式:金融应用

# 金融应用:筛选符合条件的股票
stocks = [
    {'name': '茅台', 'pe': 45},
    {'name': '五粮液', 'pe': 35},
    {'name': '招行', 'pe': 8}
]

# 找出PE<20的股票
value_stocks = [s for s in stocks if s['pe'] < 20]
print(f'价值股: {[s["name"] for s in value_stocks]}')

# 提取所有股票名称
names = [s['name'] for s in stocks]
print(f'所有股票: {names}')
价值股: ['招行']
所有股票: ['茅台', '五粮液', '招行']

⭐ while循环:基本语法

Listing 3
# 场景:监控股价直到达到目标价
# 定义初始变量
price = 18.00   # 当前价格
target = 20.00  # 目标价格
days = 0        # 已监控天数

# while循环:当条件为True时重复执行
# 语法: while condition:
# 注意:必须确保循环能够终止,否则会形成无限循环

while price < target:
    # 每天价格增长0.50元
    # += 是增量赋值操作符,等价于 price = price + 0.50
    price += 0.50

    # 天数加1
    days += 1

    # 输出当前状态
    # :.2f 表示格式化为两位小数
    print(f'第{days}天, 价格: {price:.2f}元')

    # 安全措施:防止无限循环
    # 如果超过100天仍未达到目标价,强制退出
    if days > 100:
        print('达到最大监控天数')
        break  # break语句立即跳出循环

print(f'\n达到目标价耗时{days}天')
第1天, 价格: 18.50元
第2天, 价格: 19.00元
第3天, 价格: 19.50元
第4天, 价格: 20.00元

达到目标价耗时4天

⭐ while vs for 循环的选择

场景 推荐 原因
遍历序列 for 自动迭代,简洁安全
已知迭代次数 for + range() 明确次数
未知迭代次数 while 根据条件决定
等待某个条件满足 while 灵活控制

核心原则:能用 for 就用 forwhile 留给”不确定何时结束”的场景。

⭐ while循环常见模式

# 模式1:计数器循环
count = 0
while count < 3:
    print(f'计数: {count}')
    count += 1

# 模式2:while-else结构
# else在循环正常结束时执行(break跳出则不执行)
count = 0
while count < 3:
    count += 1
else:
    print(f'循环正常结束, count={count}')
计数: 0
计数: 1
计数: 2
循环正常结束, count=3

⭐ 金融应用:蒙特卡洛模拟

import random

# 模拟股价路径,直到达到止损或止盈
random.seed(42)  # 设置随机种子,保证结果可复现
price = 100
stop_loss = 95     # 止损价
take_profit = 105  # 止盈价
days = 0

while stop_loss < price < take_profit:
    # 每日收益率服从正态分布(均值0.001,标准差0.02)
    daily_return = random.gauss(0.001, 0.02)
    price *= (1 + daily_return)
    days += 1

    if days > 252:  # 最多模拟一年(252个交易日)
        break

if price >= take_profit:
    print(f'第{days}天止盈, 价格: {price:.2f}元')
elif price <= stop_loss:
    print(f'第{days}天止损, 价格: {price:.2f}元')
else:
    print(f'第{days}天未达目标, 价格: {price:.2f}元')
第29天止盈, 价格: 106.47元

⭐ 循环控制:break语句

Listing 4
# 1. break语句:立即跳出循环
# 场景:查找第一个负收益并判断是否需要止损
returns = [0.05, -0.02, 0.01, -0.015, 0.03]

print('查找负收益:')
# enumerate()同时获取索引和值
for i, ret in enumerate(returns):
    # 检查是否为负收益
    if ret < 0:
        # :.2% 表示格式化为百分比,保留两位小数
        print(f'  第{i+1}天: {ret:.2%}')

        # 检查是否需要止损(亏损超过1%)
        if ret < -0.01:
            print('  触发止损,停止监控')
            break  # 立即跳出循环,不再检查后续元素

# 2. continue语句:跳过本次迭代,继续下一次
print('\n正收益日:')
for ret in returns:
    # 如果是负收益,跳过本次迭代
    if ret < 0:
        continue  # 直接进入下一次循环,不执行后面的代码

    # 只有正收益才会执行这里
    print(f'  {ret:.2%}')
查找负收益:
  第2天: -2.00%
  触发止损,停止监控

正收益日:
  5.00%
  1.00%
  3.00%

⭐ break与continue对比

语句 作用 适用场景
break 立即跳出整个循环 找到目标后停止搜索
continue 跳过本次迭代,继续下一次 过滤不符合条件的元素
pass 什么都不做(占位符) 空函数体、暂未实现的逻辑
  • break 只跳出最近一层循环
  • continue 跳过当前迭代的剩余代码

⭐ break只跳出最内层循环

# break只跳出最近的一层循环
for i in range(3):
    for j in range(3):
        if j == 1:
            break  # 只跳出内层循环
        print(f'i={i}, j={j}')
# 每次内层循环在j=1时中断
# 外层循环继续执行
i=0, j=0
i=1, j=0
i=2, j=0

⭐ pass语句:占位符

# pass语句什么都不做,用作占位符

# 场景1:空函数体(待实现)
def placeholder_function():
    pass  # TODO: 待实现

# 场景2:条件分支占位
price = 50
if price > 100:
    pass  # 待添加处理逻辑
else:
    print(f'价格{price}元,低于100元')

# pass在开发阶段非常有用,保持代码结构完整
价格50元,低于100元

⭐ 综合应用:股票筛选策略

Listing 5
# 模拟股票数据
# 每个股票是一个字典,包含代码、价格、PE等属性
stocks = [
    {'code': '600519.SH', 'price': 1850, 'pe': 45},
    {'code': '000858.SZ', 'price': 220, 'pe': 35},
    {'code': '600036.SH', 'price': 45, 'pe': 8}
]

# 筛选策略:价格<500且PE<40的价值股
print('筛选结果:')

# 遍历所有股票
for stock in stocks:
    # 条件1:价格<500(排除高价股)
    if stock['price'] >= 500:
        # continue:跳过本次迭代,继续下一个股票
        continue

    # 条件2:PE<40(合理估值)
    # 只有价格条件满足的股票才会到达这里
    if stock['pe'] < 40:
        # 两个条件都满足,输出股票信息
        print(f"  {stock['code']}: 价格={stock['price']}元, PE={stock['pe']}")
    # 如果PE>=40,什么都不做,继续下一个股票
筛选结果:
  000858.SZ: 价格=220元, PE=35
  600036.SH: 价格=45元, PE=8

⭐ 筛选逻辑的多种写法

stocks = [
    {'code': '600519.SH', 'price': 1850, 'pe': 45},
    {'code': '000858.SZ', 'price': 220, 'pe': 35},
    {'code': '600036.SH', 'price': 45, 'pe': 8}
]

# 方法1:单个复合条件
print('方法1 - 复合条件:')
for stock in stocks:
    if stock['price'] < 500 and stock['pe'] < 40:
        print(f"  {stock['code']}: PE={stock['pe']}")

# 方法2:列表推导式(最Pythonic)
print('\n方法2 - 列表推导式:')
filtered = [s for s in stocks if s['price'] < 500 and s['pe'] < 40]
for stock in filtered:
    print(f"  {stock['code']}: PE={stock['pe']}")
方法1 - 复合条件:
  000858.SZ: PE=35
  600036.SH: PE=8

方法2 - 列表推导式:
  000858.SZ: PE=35
  600036.SH: PE=8

⭐ 多策略组合筛选

stocks = [
    {'code': '600519.SH', 'price': 1850, 'pe': 45, 'pb': 10, 'dividend': 1.5},
    {'code': '000858.SZ', 'price': 220, 'pe': 35, 'pb': 8, 'dividend': 1.2},
    {'code': '600036.SH', 'price': 45, 'pe': 8, 'pb': 0.8, 'dividend': 2.5},
    {'code': '601318.SH', 'price': 50, 'pe': 9, 'pb': 0.9, 'dividend': 2.0}
]

# 策略1:价值投资(低PE低PB)
print('=== 价值投资策略 ===')
for stock in stocks:
    if stock['pe'] < 20 and stock['pb'] < 1:
        print(f"  {stock['code']}: PE={stock['pe']}, PB={stock['pb']}")

# 策略2:红利策略(高股息)
print('\n=== 红利策略 ===')
for stock in stocks:
    if stock['dividend'] > 2.0:
        print(f"  {stock['code']}: 股息率={stock['dividend']}%")
=== 价值投资策略 ===
  600036.SH: PE=8, PB=0.8
  601318.SH: PE=9, PB=0.9

=== 红利策略 ===
  600036.SH: 股息率=2.5%

⭐ 动态止损策略

positions = [
    {'code': '600519.SH', 'entry_price': 1800, 'current_price': 1850},
    {'code': '000858.SZ', 'entry_price': 230, 'current_price': 220},
    {'code': '600036.SH', 'entry_price': 48, 'current_price': 45}
]

print('止损信号:')
for pos in positions:
    # 计算收益率
    ret = (pos['current_price'] - pos['entry_price']) / pos['entry_price']
    # 计算亏损金额
    loss = pos['entry_price'] - pos['current_price']

    # 检查止损条件
    if ret < -0.05 and loss > 3:
        print(f"  {pos['code']}: 亏损{ret:.2%}, 亏损{loss:.0f}元, 建议止损")
    elif ret < 0:
        print(f"  {pos['code']}: 亏损{ret:.2%}, 继续持有")
    else:
        print(f"  {pos['code']}: 盈利{ret:.2%}, 继续持有")
止损信号:
  600519.SH: 盈利2.78%, 继续持有
  000858.SZ: 亏损-4.35%, 继续持有
  600036.SH: 亏损-6.25%, 继续持有

⭐ 最佳实践总结

场景 推荐方案
遍历序列 for 循环
等待条件 while 循环
已知次数 for + range()
简洁筛选 列表推导式

避免常见陷阱

  • 无限循环:确保 while 循环有终止条件
  • 修改遍历对象:不要在遍历时修改列表
  • 过深嵌套:超过3层嵌套考虑重构
  • 性能:将最严格的条件放在前面,尽早排除

⭐ 平台任务解答代码

Listing 6
# 注:该代码块存在缩进错误且读取远程平台数据文件,渲染时无法执行
# ⚠️ 平台原始代码 - 请原样输入至教学平台(注释除外),平台才会判定答案正确
#任务一
import pandas as pd

# 从Excel文件读取数据存入data
data=pd.read_excel("https://huoran.oss-cn-shenzhen.aliyuncs.com/1725522421307.xlsx")

DJ_index = []  # 定义列表DJ_index
HS_index= []  # 定义列表HS_index
SH_index = []  # 定义列表SH_index
SZ_index = []  # 定义列表SZ_index

for i in data["道琼斯工业平均指数"]:  # 遍历data["道琼斯工业平均指数"]中的每个i
    DJ_index.append(i)  # 将道琼斯指数涨跌幅数据添加到列表
  
for i in data["恒生指数"]:     #获取恒生指数的日涨跌幅列表
    HS_index.append(i)  # 将恒生指数涨跌幅数据添加到列表

for i in data["上证综指"]:     #获取上证综指的日涨跌幅列表
    SH_index.append(i)  # 将上证综指涨跌幅数据添加到列表
  
for i in data["深证成指"]:  # 遍历data["深证成指"]中的每个i
    SZ_index.append(i)  # 将深证成指涨跌幅数据添加到列表

print(DJ_index)  # 输出指数数据
print(HS_index)  # 输出指数数据
print(SH_index)  # 输出指数数据
print(SZ_index)  # 输出指数数据


#任务二
import pandas as pd

# 从Excel文件读取数据存入data
data=pd.read_excel("https://huoran.oss-cn-shenzhen.aliyuncs.com/1725522421307.xlsx")

DJ_index = []  # 定义列表DJ_index
for i in data["道琼斯工业平均指数"]:  # 遍历data["道琼斯工业平均指数"]中的每个i
    DJ_index.append(i)  # 将道琼斯指数涨跌幅数据添加到列表

for i in DJ_index:  # 遍历DJ_index中的每个i
  if i < -0.006:    #如果小于-0.006
    break  # 跳出循环
  print("已经访问的道琼斯工业平均指数日涨跌幅数据",i)  # 输出已经访问的道琼斯工业平均指数日涨跌幅数据
  
#任务三
import pandas as pd

# 从Excel文件读取数据存入data
data=pd.read_excel("https://huoran.oss-cn-shenzhen.aliyuncs.com/1725522421307.xlsx")

HS_index = []  # 定义列表HS_index

for i in data["恒生指数"]:  # 遍历data["恒生指数"]中的每个i
    HS_index.append(i)  # 将恒生指数涨跌幅数据添加到列表
for i in HS_index:  # 遍历HS_index中的每个i
  if i > 0:       #大于零的话
    pass  # 占位符,暂不执行任何操作
  else:  # 不满足以上条件时
    print("恒生指数的日下跌数据(负数)",i)  # 输出恒生指数的日下跌数据(负数)

 
#任务四
import pandas as pd

# 从Excel文件读取数据存入data
data=pd.read_excel("https://huoran.oss-cn-shenzhen.aliyuncs.com/1725522421307.xlsx")

SH_index = []  # 定义列表SH_index
SZ_index = []  # 定义列表SZ_index

for i in data["上证综指"]:  # 遍历data["上证综指"]中的每个i
    SH_index.append(i)  # 将上证综指涨跌幅数据添加到列表
  
for i in data["深证成指"]:  # 遍历data["深证成指"]中的每个i
    SZ_index.append(i)  # 将深证成指涨跌幅数据添加到列表
  
R_SH = []        #创建一个R_SH的空列表
for i in SH_index:  # 遍历SH_index中的每个i
  if i < -0.1:     #如果小于-0.1
    pass  # 占位符,暂不执行任何操作
  elif i > 0.01:    #如果大于0.01
    pass  # 占位符,暂不执行任何操作
  else:  # 不满足以上条件时
        R_SH.append(i)  # 将上证综指筛选后的涨跌幅数据添加到列表
print("上证综指的日涨跌幅处于-1%至1%区间的列表\n",R_SH)  # 输出上证综指的日涨跌幅处于-1%至1%区间的列表\n